package com.hz.common.generator;

import java.io.File;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.Element;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.config.CommentGeneratorConfiguration;
import org.mybatis.generator.config.Context;

import com.hz.common.freemarker.EditFile;
import com.hz.common.freemarker.IInterface;
import com.hz.common.freemarker.IInterfaceNoId;
import com.hz.common.freemarker.IService;
import com.hz.common.freemarker.PropertiesUtil;
import com.hz.common.freemarker.Service;
import com.hz.common.freemarker.TemplateValue;

/**
 * 自定义插件
 * @author CZH
 */
public class GenPlugin extends PluginAdapter {
	private final static PropertiesUtil propertiesUtil = new PropertiesUtil();
	
	private Set<String> mappers = new HashSet<String>();
	// 注释生成器
	private CommentGeneratorConfiguration commentCfg;

	@Override
	public boolean validate(List<String> warnings) {
		return true;
	}

	@Override
	public void setContext(Context context) {
		super.setContext(context);
		// 设置默认的注释生成器
		commentCfg = new CommentGeneratorConfiguration();
		commentCfg.setConfigurationType(GenCommentGenerator.class.getCanonicalName());
		context.setCommentGeneratorConfiguration(commentCfg);
		// 支持oracle获取注释#114
		context.getJdbcConnectionConfiguration().addProperty("remarksReporting", "true");
	}

	@Override
	public void setProperties(Properties properties) {
		super.setProperties(properties);
		String mappers = this.properties.getProperty("mappers");
		for (String mapper : mappers.split(",")) {
			this.mappers.add(mapper);
		}
	}

	/**
	 * 生成的Mapper接口
	 * @param interfaze
	 * @param topLevelClass
	 * @param introspectedTable
	 * @return
	 */
	@Override
	public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		// 获取实体类
		FullyQualifiedJavaType entityType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
		// import接口
		for (String mapper : mappers) {
			interfaze.addImportedType(new FullyQualifiedJavaType(mapper));
			interfaze.addSuperInterface(new FullyQualifiedJavaType(mapper + "<" + entityType.getShortName() + ">"));
		}
		// import实体类
		interfaze.addImportedType(entityType);
		return true;
	}

	/**
	 * 拼装SQL语句生成Mapper接口映射文件
	 */
	@Override
	public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
		XmlElement rootElement = document.getRootElement();
		// 数据库表名
		String tableName = introspectedTable.getFullyQualifiedTableNameAtRuntime();
		List<IntrospectedColumn> pkColumnList = introspectedTable.getPrimaryKeyColumns();
		// 主键
		IntrospectedColumn pkColumn = null;
		if(pkColumnList!=null&&pkColumnList.size()>0)
			pkColumn = introspectedTable.getPrimaryKeyColumns().get(0);
		
		// 公共字段
		StringBuilder columnSQL = new StringBuilder();
		// IF判断语句
		StringBuilder ifSQL = new StringBuilder();
		// 要插入的字段(排除自增主键)
		StringBuilder saveColumn = new StringBuilder("insert into ").append(tableName).append("(\n");
		// 要保存的值
		StringBuilder saveValue = new StringBuilder("(\n");
		StringBuilder insertColumn = new StringBuilder("insert into ").append(tableName).append("(\n\t  ");
		StringBuilder insertValue = new StringBuilder("\n\t(\n\t  ");
		// 拼装更新字段
		StringBuilder updateSQL = null;
		if(pkColumn!=null)
			updateSQL = new StringBuilder("update ").append(tableName).append(" set \n");

		boolean isUpdateTime = false;
		// 数据库字段名
		String columnName = null;
		// java字段名
		String javaProperty = null;
		for (IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()) {
			columnName = MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn);
			if(columnName.equals("update_time")) isUpdateTime = true;
			javaProperty = introspectedColumn.getJavaProperty();
			// 拼接字段
			columnSQL.append(columnName).append(",");
			// 拼接IF语句
			if(columnName.equals("parent_id")){
				ifSQL.append("      <if test=\"null != entity.").append(javaProperty).append("\">");
			}else{
				ifSQL.append("      <if test=\"null != entity.").append(javaProperty).append(" and '' != entity.").append(javaProperty).append("\">");
			}
			ifSQL.append("and ").append(columnName).append(" = #{entity.").append(javaProperty).append("}</if>\n");

			// 拼接SQL
			if (!introspectedColumn.isAutoIncrement()) {
				saveColumn.append("\t  <if test=\"null != item.").append(javaProperty).append("\">, ").append(columnName).append("</if>\n");

				saveValue.append("\t  <if test=\"null != item.").append(javaProperty).append("\">, ").append("#{item.").append(javaProperty)
						.append("}</if>\n");
				
				insertColumn.append(","+columnName);
				insertValue.append(",#{"+javaProperty+",jdbcType="+introspectedColumn.getJdbcTypeName()+"}");

				// 时间格式用now()作为值
				/*
				 * if(Types.TIMESTAMP == introspectedColumn.getJdbcType()){
				 * saveValue.append(", now()"); }else{ saveValue.append(
				 * ", #{item.").append(javaProperty).append("}"); }
				 */
				if(pkColumn!=null){
					updateSQL.append("      <if test=\"null != item.").append(javaProperty).append("\">");
					updateSQL.append(", ").append(columnName).append(" = #{item.").append(javaProperty).append("}</if>\n");
				}
			}
		}
		String columns = columnSQL.substring(0, columnSQL.length() - 1);
		rootElement.addElement(createSql("Base_Column_List", columns));

		String whereSQL = MessageFormat.format("<where>\n{0}\t</where>", ifSQL.toString());
		rootElement.addElement(createSql("sql_where", whereSQL));
		//自定义类准备开始
		TemplateValue tv = new TemplateValue();
		tv.setEntityName(introspectedTable.getBaseRecordType()
				.substring(introspectedTable.getBaseRecordType().lastIndexOf(".") + 1));
		tv.setPackageName(propertiesUtil.getPackageName());
		tv.setDeclareName(lowerFirst(tv.getEntityName()));
		tv.setUpperName(tv.getEntityName().toUpperCase());
		tv.setAuthor(propertiesUtil.getAuthor());
		tv.setDate(new SimpleDateFormat("yyyy年MM月dd日").format(new Date()));
		String iServicePath = propertiesUtil.getiServicePath();
		try {
			isexitsPath(iServicePath);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		String servicePath = propertiesUtil.getServicePath();
		try {
			isexitsPath(servicePath);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		String iInterfacePath = propertiesUtil.getiInterfacePath();
		try {
			isexitsPath(iInterfacePath);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		String interfacePath = propertiesUtil.getInterfacePath();
		try {
			isexitsPath(interfacePath);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//自定义类准备结束
		if(pkColumn!=null){
			rootElement.addElement(createSelect("selectByPrimaryKey", tableName, pkColumn));
			updateSQL.append("\twhere ").append(pkColumn.getActualColumnName()).append(" = #{item.").append(pkColumn.getJavaProperty()).append("}");
			rootElement.addElement(createDels(tableName, pkColumn, "delArray", "array"));
			rootElement.addElement(createDels(tableName, pkColumn, "delList", "list"));
			rootElement.addElement(createSql("sql_update", updateSQL.toString().replaceFirst(",","")));
			rootElement.addElement(createUpdate("updateByPrimaryKey"));
			rootElement.addElement(createUpdate("batchUpdate"));
			//有id主键自定义类准备开始
			new IService().generateRequest(tv);
			new Service().generateRequest(tv);
			new IInterface().generateRequest(tv);
			new com.hz.common.freemarker.Interface().generateRequest(tv);
			updateHzOperateCode(tv.getUpperName(),tv.getEntityName());
			//有id主键自定义类准备结束
		}else{
			//无id主键自定义类准备开始
			new IService().generateRequest(tv);
			new Service().generateRequest(tv);
			new IInterfaceNoId().generateRequest(tv);
			new com.hz.common.freemarker.InterfaceNoId().generateRequest(tv);
			updateHzOperateCodeNoId(tv.getUpperName(),tv.getEntityName());
			//无id主键自定义类准备结束
		}
		rootElement.addElement(createInsert("insert", insertColumn.append("\n\t) values").toString().replaceFirst(",",""),insertValue.append("\n\t)").toString().replaceFirst(",",""),introspectedTable));
		rootElement.addElement(createInsertSelective("insertSelective"));
		rootElement.addElement(createSave("batchSave", null));
//		rootElement.addElement(createSelect("selectOne", tableName, null));
//		rootElement.addElement(createSelect("selectList", tableName, null));
		rootElement.addElement(createQueryForList("queryForList", tableName, isUpdateTime));
		rootElement.addElement(createCount("getCount", tableName));

		rootElement.addElement(createSql("sql_save_columns", saveColumn.append("\t) values").toString().replaceFirst(",", "")));
		rootElement.addElement(createSql("sql_save_values", saveValue.append("\t)").toString().replaceFirst(",", "")));
		
		return super.sqlMapDocumentGenerated(document, introspectedTable);
	}

	private void updateHzOperateCodeNoId(String upperName, String entityName) {
		String content = "	/** 新增" + entityName + " */\n	ADD_" + upperName + "(\"ADD_" + upperName + "\", \"新增" + entityName + "\"),\n\n"
				+ "	/** 分页查询" + entityName + " */\n	QUERY_" + upperName + "_PAGE(\"QUERY_" + upperName + "_PAGE\", \"分页查询" + entityName + "\"),\n\n"
				+ "	/** 查询" + entityName + "列表 */\n	QUERY_" + upperName + "_LIST(\"QUERY_" + upperName + "_LIST\", \"查询" + entityName + "列表\"),\n\n"
				+ "	/** 选定数据添加" + entityName + " */\n	INSERTSELECTIVE_" + upperName + "(\"INSERTSELECTIVE_" + upperName + "\", \"选定数据添加" + entityName + "\"),\n\n"
				+ "	;";
		String[] str = EditFile.readString(propertiesUtil.getHzOperateCodePath()).split("	;");
		try {
			EditFile.writeTxtFile(str[0]+content+str[1],propertiesUtil.getHzOperateCodePath());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 是否创建目录
	 * 
	 * @param path
	 * @return
	 */
	public boolean isexitsPath(String path) throws InterruptedException {
		String[] paths = path.split("\\\\");
		StringBuffer fullPath = new StringBuffer();
		for (int i = 0; i < paths.length; i++) {
			fullPath.append(paths[i]).append("\\\\");
			File file = new File(fullPath.toString());
			if (!file.exists()) {
				file.mkdir();
				Thread.sleep(1500);
			}
		}
		File file = new File(fullPath.toString());
		if (!file.exists()) {
			return true;
		} else {
			return false;
		}
	}

	private void updateHzOperateCode(String upperName,String entityName) {
		String content = "	/** 查询" + entityName + " */\n	QUERY_" + upperName + "(\"QUERY_" + upperName + "\", \"查询" + entityName + "\"),\n\n"
				+ "	/** 新增" + entityName + " */\n	ADD_" + upperName + "(\"ADD_" + upperName + "\", \"新增" + entityName + "\"),\n\n"
				+ "	/** 分页查询" + entityName + " */\n	QUERY_" + upperName + "_PAGE(\"QUERY_" + upperName + "_PAGE\", \"分页查询" + entityName + "\"),\n\n"
				+ "	/** 查询" + entityName + "列表 */\n	QUERY_" + upperName + "_LIST(\"QUERY_" + upperName + "_LIST\", \"查询" + entityName + "列表\"),\n\n"
				+ "	/** 更新" + entityName + " */\n	UPDATE_" + upperName + "(\"UPDATE_" + upperName + "\", \"更新" + entityName + "\"),\n\n"
				+ "	/** 数组批量删除" + entityName + " */\n	DELETE_" + upperName + "(\"DELETE_" + upperName + "\", \"数组批量删除" + entityName + "\"),\n\n"
				+ "	/** 选定数据添加" + entityName + " */\n	INSERTSELECTIVE_" + upperName + "(\"INSERTSELECTIVE_" + upperName + "\", \"选定数据添加" + entityName + "\"),\n\n"
				+ "	/** 批量删除" + entityName + " */\n	DELETELIST_" + upperName + "(\"DELETELIST_" + upperName + "\", \"批量删除" + entityName + "\"),\n\n"
				+ "	/** 批量更新" + entityName + " */\n	BATCHUPDATE_" + upperName + "(\"BATCHUPDATE_" + upperName + "\", \"批量更新" + entityName + "\"),\n\n"
				+ "	/** 批量保存" + entityName + " */\n	BATCHSAVE_" + upperName + "(\"BATCHSAVE_" + upperName + "\", \"批量保存" + entityName + "\"),\n\n"
				+ "	;";
		String[] str = EditFile.readString(propertiesUtil.getHzOperateCodePath()).split("	;");
		try {
			EditFile.writeTxtFile(str[0]+content+str[1],propertiesUtil.getHzOperateCodePath());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static String lowerFirst(String oldStr) {
		char[] chars = oldStr.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}

	private Element createCount(String id, String tableName) {
		XmlElement select = new XmlElement("select");
		select.addAttribute(new Attribute("id", id));
		select.addAttribute(new Attribute("resultType", "java.lang.Integer"));
		select.addAttribute(new Attribute("parameterType", "com.hz.base.beans.vo.QueryParams"));
		StringBuilder selectStr = new StringBuilder("<choose>\n\t  "
				+ "<when test=\"(groupColumn != null and '' != groupColumn) || (distinctColumn != null and '' != distinctColumn)\">\n\t\t"
				+ "SELECT COUNT(*) FROM (select\n\t\t  "
				+ "<choose>\n\t\t\t"
				+ "<when test=\"distinctColumn != null and '' != distinctColumn\">distinct ${distinctColumn}</when>\n\t\t\t"
				+ "<otherwise>count(*)</otherwise>\n\t\t  "
				+ "</choose>\n\t\t"
				+ "from ").append(tableName).append(" <include refid=\"sql_where\" />\n\t\t"
				+ "<if test=\"groupColumn != null and '' != groupColumn\">group by ${groupColumn}</if>) as a\n\t  "
				+ "</when>\n\t  "
				+ "<otherwise>SELECT COUNT(*) FROM ").append(tableName).append(" <include refid=\"sql_where\" /></otherwise>\n\t"
				+ "</choose>");
		select.addElement(new TextElement(selectStr.toString()));
		return select;
	}

	/**
	 * 公共SQL
	 * @param id
	 * @param sqlStr
	 * @return
	 */
	private XmlElement createSql(String id, String sqlStr) {
		XmlElement sql = new XmlElement("sql");
		sql.addAttribute(new Attribute("id", id));
		sql.addElement(new TextElement(sqlStr));
		return sql;
	}

	/**
	 * 查询
	 * @param id
	 * @param tableName
	 * @param pkColumn
	 * @return
	 */
	private XmlElement createSelect(String id, String tableName, IntrospectedColumn pkColumn) {
		XmlElement select = new XmlElement("select");
		select.addAttribute(new Attribute("id", id));
		select.addAttribute(new Attribute("resultMap", "BaseResultMap"));
		select.addAttribute(new Attribute("parameterType", "java.lang.Long"));

		StringBuilder selectStr = new StringBuilder("select\n\t  <include refid=\"Base_Column_List\" />\n\t  from\n\t  ").append(tableName);
		if (null != pkColumn) {
			selectStr.append("\n\twhere ").append(pkColumn.getActualColumnName()).append(" = #{").append(pkColumn.getJavaProperty()).append("}");
		} else {
			selectStr.append("\n\t  <include refid=\"sql_where\" />");
		}
		select.addElement(new TextElement(selectStr.toString()));
		return select;
	}
	
	/**
	 * 查询 queryForList
	 * @param id
	 * @param tableName
	 * @param pkColumn
	 * @return
	 */
	private XmlElement createQueryForList(String id, String tableName, boolean isUpdateTime) {
		XmlElement select = new XmlElement("select");
		select.addAttribute(new Attribute("id", id));
		select.addAttribute(new Attribute("resultMap", "BaseResultMap"));
		select.addAttribute(new Attribute("parameterType", "com.hz.base.beans.vo.QueryParams"));
		
		StringBuilder selectStr = new StringBuilder("select\n\t  "
				+ "<choose>\n\t\t<when test=\"distinctColumn != null and '' != distinctColumn\">\n\t\t  "
				+ "distinct ${distinctColumn}\n\t\t"
				+ "</when>\n\t\t"
				+ "<otherwise><include refid=\"Base_Column_List\" /></otherwise>\n\t  "
				+ "</choose>\n\t  "
				+ "from\n\t  ").append(tableName);
		selectStr.append("\n\t  <include refid=\"sql_where\" />\n\t  "
				+ "<if test=\"groupColumn != null and '' != groupColumn\">group by ${groupColumn}</if>\n\t  "
				+ (isUpdateTime?"<choose>\n\t\t<when test=\"orderColumn != null and '' != orderColumn\">order by ${orderColumn}</when>\n\t\t"
						+ "<otherwise>order by update_time desc</otherwise>\n\t  </choose>\n\t  "
						:"<if test=\"orderColumn != null and '' != orderColumn\">order by ${orderColumn}</if>\n\t  "));
		selectStr.append("<if test=\"paging != null\">limit ${paging.startPos},${paging.pageSize}</if>");
		select.addElement(new TextElement(selectStr.toString()));
		return select;
	}
	


	private Element createInsert(String id, String saveColumn, String insertValue, IntrospectedTable introspectedTable) {
		XmlElement save = new XmlElement("insert");
		save.addAttribute(new Attribute("id", id));
		save.addAttribute(new Attribute("parameterType", "" + (new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()))));
		save.addElement(new TextElement(saveColumn+insertValue));
		return save;
	}

	/**
	 * 保存
	 * @param id
	 * @param pkColumn
	 * @return
	 */
	private XmlElement createInsertSelective(String id) {
		XmlElement save = new XmlElement("insert");
		save.addAttribute(new Attribute("id", id));
		save.addElement(new TextElement("<include refid=\"sql_save_columns\" /><include refid=\"sql_save_values\" />"));
		return save;
	}
	
	/**
	 * 保存
	 * @param id
	 * @param pkColumn
	 * @return
	 */
	private XmlElement createSave(String id, IntrospectedColumn pkColumn) {
		XmlElement save = new XmlElement("insert");
		save.addAttribute(new Attribute("id", id));
		if (null != pkColumn) {
			save.addAttribute(new Attribute("keyProperty", "item." + pkColumn.getJavaProperty()));
			save.addAttribute(new Attribute("useGeneratedKeys", "true"));
			save.addElement(new TextElement("<include refid=\"sql_save_columns\" /><include refid=\"sql_save_values\" />"));
		} else {
			StringBuilder saveStr = new StringBuilder(
					"<foreach collection=\"list\" index=\"index\" item=\"item\" open=\"\" separator=\";\" close=\"\">\n\t  ")
							.append("<include refid=\"sql_save_columns\" /><include refid=\"sql_save_values\" />\n\t</foreach>");
			save.addElement(new TextElement(saveStr.toString()));
		}
		return save;
	}

	/**
	 * 更新
	 * @param id
	 * @return
	 */
	private XmlElement createUpdate(String id) {
		XmlElement update = new XmlElement("update");
		update.addAttribute(new Attribute("id", id));
		if ("updateByPrimaryKey".equals(id)) {
			update.addElement(new TextElement("<include refid=\"sql_update\" />"));
		} else {
			update.addElement(new TextElement(
					"<foreach collection=\"list\" index=\"index\" item=\"item\" open=\"\" separator=\";\" close=\"\">\n\t  <include refid=\"sql_update\" />\n\t</foreach>"));
		}
		return update;
	}

	/**
	 * 删除
	 * @param tableName
	 * @param pkColumn
	 * @param method
	 * @param type
	 * @return
	 */
	private XmlElement createDels(String tableName, IntrospectedColumn pkColumn, String method, String type) {
		XmlElement delete = new XmlElement("delete");
		delete.addAttribute(new Attribute("id", method));
		StringBuilder deleteStr = new StringBuilder("delete from ");
		if(method.equals("delList")){
			deleteStr = deleteStr.append(tableName).append(" where ").append(pkColumn.getActualColumnName())
					.append(" in\n\t")
					.append("<foreach collection=\"").append(type)
					.append("\" index=\"index\" item=\"item\" open=\"(\" separator=\",\" close=\")\">#{item.id}</foreach>");
		}else{
			deleteStr = deleteStr.append(tableName).append(" where ").append(pkColumn.getActualColumnName())
					.append(" in\n\t")
					.append("<foreach collection=\"").append(type)
					.append("\" index=\"index\" item=\"item\" open=\"(\" separator=\",\" close=\")\">#{item}</foreach>");
		}
		delete.addElement(new TextElement(deleteStr.toString()));
		return delete;
	}

	// 以下设置为false,取消生成默认增删查改xml
	@Override
	public boolean clientDeleteByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientInsertMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientSelectAllMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientSelectByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientUpdateByPrimaryKeyWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapDeleteByPrimaryKeyElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapInsertElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapSelectAllElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapSelectByPrimaryKeyElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

}
